/*
 * ZCL Global Command
 * */

#include "app/framework/include/af.h"
#include "zcl.h"

/*
 * Read Attributes
 * */
//send read-attributes-request command
uint16_t zcl_read_attributes_fill_buffer(uint16_t clusterId,
                                         uint16_t manufacturerCode,
                                         uint8_t sequence,
                                         uint8_t direction,
                                         uint8_t num,
                                         zclReadAttr_t *attrs)
{
  uint8_t buf[ZCL_MAX_PARAM_LENGTH];
  uint8_t len = 0;
  uint8_t maxLen = ZCL_MAX_PARAM_LENGTH;
  uint8_t n;
  uint8_t *p = buf;

  for(n=0; n<num; n++) {
    if(len >= maxLen) {
      break;
    }
    *p++ = LOW_BYTE(attrs[n]);
    *p++ = HIGH_BYTE(attrs[n]);
    len+=2;
  }
  return sl_zigbee_af_fill_external_manufacturer_specific_buffer_white_sequence(ZCL_GLOBAL_COMMAND|direction,
                                                                                clusterId,
                                                                                manufacturerCode,
                                                                                sequence,
                                                                                ZCL_READ_ATTRIBUTES_COMMAND_ID,
                                                                                "b",
                                                                                buf,
                                                                                len);
}

//receive read-attributes-response command
uint8_t zcl_read_attributes_response_parse(uint8_t *buf, zclReadRsp_t *attrs)
{
  attrs->attrId = HIGH_LOW_TO_INT(buf[1], buf[0]);
  attrs->status = buf[2];
  if(attrs->status == 0) {
    attrs->dataType = buf[3];
    attrs->dataPtr = buf+4;
    attrs->dataSize = sl_zigbee_af_attribute_value_size(attrs->dataType, attrs->dataPtr, 32);
    return 4 + attrs->dataSize;
  } else {
    attrs->dataType = ZCL_UNKNOWN_ATTRIBUTE_TYPE;
    attrs->dataSize = 0;
    return 3;
  }
}

/*
 * Write Attributes
 * */
//send write-attributes-request command
uint16_t zcl_write_attributes_fill_buffer(uint16_t clusterId,
                                          uint16_t manufacturerCode,
                                          uint8_t sequence,
                                          uint8_t direction,
                                          uint8_t num,
                                          zclWriteAttr_t *attrs)
{
  uint8_t buf[ZCL_MAX_PARAM_LENGTH];
  uint8_t len = 0;
  uint8_t maxLen = ZCL_MAX_PARAM_LENGTH;
  uint8_t n;
  uint8_t *p = buf;

  for(n=0; n<num; n++) {
    uint8_t size = 0;
    if(len >= maxLen) {
      break;
    }
    *p++ = LOW_BYTE(attrs[n].attrId);
    *p++ = HIGH_BYTE(attrs[n].attrId);
    *p++ = attrs[n].dataType;
    size = sl_zigbee_af_attribute_value_size(attrs[n].dataType, attrs[n].dataPtr, attrs[n].dataSize);
    memcpy(p,attrs[n].dataPtr,size);
    p += size;
    len+=(3 + size);
  }

  return sl_zigbee_af_fill_external_manufacturer_specific_buffer_white_sequence(ZCL_GLOBAL_COMMAND|direction,
                                                                                clusterId,
                                                                                manufacturerCode,
                                                                                sequence,
                                                                                ZCL_WRITE_ATTRIBUTES_COMMAND_ID,
                                                                                "b",
                                                                                buf,
                                                                                len);
}

//receive write-attributes-response command
uint8_t zcl_write_attributes_response_parse(uint8_t *buf, zclWriteRsp_t *attrs)
{
  attrs->status = buf[0];
  attrs->attrId = HIGH_LOW_TO_INT(buf[2],buf[1]);
  return 3;
}

/*
 * configure reporting
 * */
//send configure-reporting-request command
uint16_t zcl_configure_reporting_fill_buffer(uint16_t clusterId,
                                             uint16_t manufacturerCode,
                                             uint8_t sequence,
                                             uint8_t direction,
                                             uint8_t num,
                                             zclCfgReport_t *attrs)
{
  uint8_t buf[ZCL_MAX_PARAM_LENGTH];
  uint8_t len = 0;
  uint8_t maxLen = ZCL_MAX_PARAM_LENGTH;
  uint8_t n;
  uint8_t *p = buf;

  for(n=0; n<num; n++) {
    uint8_t size = 0;
    if(len >= maxLen) {
      break;
    }
    *p ++ = attrs[n].direction;
    *p ++ = LOW_BYTE(attrs[n].attrId);
    *p ++ = HIGH_BYTE(attrs[n].attrId);
    size = 3;
    if( attrs[n].direction == SL_ZIGBEE_ZCL_REPORTING_DIRECTION_REPORTED ) {
      *p ++ = attrs[n].dataType;
      *p ++ = LOW_BYTE(attrs[n].minReportInt);
      *p ++ = HIGH_BYTE(attrs[n].minReportInt);
      *p ++ = LOW_BYTE(attrs[n].maxReportInt);
      *p ++ = HIGH_BYTE(attrs[n].maxReportInt);
      size += 5;
      if (sl_zigbee_af_get_attribute_analog_or_discrete_type(attrs[n].dataType) == SL_ZIGBEE_AF_DATA_TYPE_ANALOG) {
        uint8_t dataSize = sl_zigbee_af_get_data_size(attrs[n].dataType);
        memcpy(p, attrs[n].changeValue, dataSize);
        p += dataSize;
        size += dataSize;
      }
    } else {   //SL_ZIGBEE_ZCL_REPORTING_DIRECTION_RECEIVED
      *p ++ = LOW_BYTE(attrs[n].timeout);
      *p ++ = HIGH_BYTE(attrs[n].timeout);
      size += 2;
    }
    len += size;
  }
  return sl_zigbee_af_fill_external_manufacturer_specific_buffer_white_sequence(ZCL_GLOBAL_COMMAND|direction,
                                                                                clusterId,
                                                                                manufacturerCode,
                                                                                sequence,
                                                                                ZCL_CONFIGURE_REPORTING_COMMAND_ID,
                                                                                "b",
                                                                                buf,
                                                                                len);
}

//receive configure-reporting-response command
uint8_t zclConfigureReportingResponseParse(uint8_t *buf, zclCfgReportRsp_t *attrs)
{
  attrs->status = buf[0];
  attrs->direction = buf[1];
  attrs->attrId = HIGH_LOW_TO_INT(buf[3],buf[2]);
  return 4;
}

/*
 * read reporting configuration
 * */
//send read-reporting-configuration-request command
uint16_t zcl_read_reporting_configuration_fill_buffer(uint16_t clusterId,
                                                      uint16_t manufacturerCode,
                                                      uint8_t sequence,
                                                      uint8_t direction,
                                                      uint8_t num,
                                                      zclReadReport_t *attrs)
{
  uint8_t buf[ZCL_MAX_PARAM_LENGTH];
  uint8_t len = 0;
  uint8_t maxLen = ZCL_MAX_PARAM_LENGTH;
  uint8_t n;
  uint8_t *p = buf;

  for(n=0; n<num; n++) {
    if(len >= maxLen) {
      break;
    }
    *p++ = attrs[n].direction;
    *p++ = LOW_BYTE(attrs[n].attrId);
    *p++ = HIGH_BYTE(attrs[n].attrId);
    len+=3;
  }
  return sl_zigbee_af_fill_external_manufacturer_specific_buffer_white_sequence(ZCL_GLOBAL_COMMAND|direction,
                                                                                clusterId,
                                                                                manufacturerCode,
                                                                                sequence,
                                                                                ZCL_READ_REPORTING_CONFIGURATION_COMMAND_ID,
                                                                                "b",
                                                                                buf,
                                                                                len);
}

//receive read-reporting-configure-response command
uint8_t zcl_read_reporting_configuration_response_parse(uint8_t *buf, zclReadReportRsp_t *attrs)
{
  attrs->status = buf[0];
  attrs->direction = buf[1];
  attrs->attrId = HIGH_LOW_TO_INT(buf[3],buf[2]);
  if( attrs->status != SL_ZIGBEE_ZCL_STATUS_SUCCESS) {
    return 4;
  }
  if (attrs->direction == SL_ZIGBEE_ZCL_REPORTING_DIRECTION_REPORTED) {
    uint8_t dataSize = 0;
    attrs->dataType = buf[4];
    attrs->minReportInt = HIGH_LOW_TO_INT(buf[6],buf[5]);
    attrs->maxReportInt = HIGH_LOW_TO_INT(buf[8],buf[7]);
    attrs->changeValue = NULL;
    attrs->timeout = 0;
    if (sl_zigbee_af_get_attribute_analog_or_discrete_type(attrs->dataType) == SL_ZIGBEE_AF_DATA_TYPE_ANALOG) {
      dataSize = sl_zigbee_af_get_data_size(attrs->dataType);
      if( dataSize > 0 )  {
        attrs->changeValue = buf + 9;
      }
    }
    return dataSize + 9;
  } else {
    attrs->dataType = 0x00;
    attrs->minReportInt = 0xFFFF;
    attrs->maxReportInt = 0xFFFF;
    attrs->changeValue = NULL;
    attrs->timeout = HIGH_LOW_TO_INT(buf[5],buf[4]);
    return 6;
  }
}

/*
 * receive reporting attributes
 * */
uint8_t zcl_report_attributes_parse(uint8_t *buf, zclReportAttr_t *attrs)
{
  attrs->attrId = HIGH_LOW_TO_INT(buf[1], buf[0]);
  attrs->dataType = buf[2];
  attrs->dataPtr = buf+3;
  attrs->dataSize = sl_zigbee_af_attribute_value_size(attrs->dataType, attrs->dataPtr, 32);
  return 3 + attrs->dataSize;
}

/*
 * discover attributes
 * */
//send discover-attributes-request & discover-attributes-extend-request command
uint16_t zcl_discover_attributes_fill_buffer(uint16_t clusterId,
                                             uint16_t manufacturerCode,
                                             uint8_t sequence,
                                             uint8_t direction,
                                             bool extend,
                                             uint8_t num,
                                             uint16_t startAttrId)
{
  uint8_t buf[3];
  uint8_t cmd = ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID;
  if(extend) {
    cmd = ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID;
  }
  buf[0] = LOW_BYTE(startAttrId);
  buf[1] = HIGH_BYTE(startAttrId);
  buf[2] = num;
  return sl_zigbee_af_fill_external_manufacturer_specific_buffer_white_sequence(ZCL_GLOBAL_COMMAND|direction,
                                                                                clusterId,
                                                                                manufacturerCode,
                                                                                sequence,
                                                                                cmd,
                                                                                "b",
                                                                                buf,
                                                                                3);
}

//receive discover-attributes-response command
uint8_t zcl_discover_attributes_response_parse(uint8_t *buf, zclDiscAttrRsp_t* attrs)
{
  attrs->attrId = HIGH_LOW_TO_INT(buf[1], buf[0]);
  attrs->dataType = buf[2];
  return 3;
}

//receive discover-attributes-extend-response command
uint8_t zcl_discover_attributes_extend_response_parse(uint8_t *buf, zclDiscAttrExtRsp_t* attrs)
{
  attrs->attrId = HIGH_LOW_TO_INT(buf[1], buf[0]);
  attrs->dataType = buf[2];
  attrs->access = buf[3];
  return 4;
}
